package ChessLike::GamePiece;
use strict;
use warnings;

=head1 NAME

ChessLike::GamePiece - The great new ChessLike::GamePiece!

=head1 VERSION

Version 0.0.1

=cut

use version; our $VERSION = qv("v0.0.1");

use Config::General qw( ParseConfig );

=head1 SYNOPSIS

Quick summary of what the module does.

Perhaps a little code snippet.

    use ChessLike::GamePiece;

    my $foo = ChessLike::GamePiece->new();
    ...

=head1 EXPORT

A list of functions that can be exported.  You can delete this section
if you don't export anything, such as for a purely object-oriented module.

=head1 API

=head2 new( $class, %options )

 Purpose: Creates a new game piece.
 Options: `name' - Name of game piece (default = 'Nameless')
          `move-vectors' - ArrayRef of vectors that define game piece
                           movement (default = [])
          `shot-vectors' - ArrayRef of vectors that define game piece
                           firing range (default = [])
          `proxy-move-field' - Array defining movement boundaries for carried
                               units (default = undef)
          `proxy-shot-field' - Array defining shot boundaries for carried units
                               (default = undef)
          `requires-units' - Name of unit that this unit moves/shoots via
                             proxy [string].  May also be an arrayref of
                             ChessLike::GamePiece objects  (default = undef)
          `carries-units' - Name of unit that this unit is a move/shot proxy
                            for [string].  May also be an arrayref of
                            ChessLike::GamePiece objects (default = undef)
          `is-flying' - Boolean value for marking flying units (default = 0)
          `can-rotate' - Boolean value for marking units that can rotate
                         (default = 0)
 Returns: $self
 Throws: none

=cut

sub new {
   my $class = shift;

   my $self = {
      'name' => 'Nameless',

      'move-vectors' => [],
      'shot-vectors' => [],

      'proxy-move-field' => undef,
      'proxy-shot-field' => undef,
      'requires-units'   => undef,
      'carries-units'    => undef,

      'is-flying'   => 0,
      'can-rotate'  => 0,
   };
   bless $self, $class;

   $self->configure( @_ ) if @_;

   return $self;
}

=head2 parse( $class, $path )

 Purpose: Creates a new game piece using configuration found at $path.
 Returns: $self
 Throws: none

=cut

sub parse {
   my ( $class, $path ) = @_;
   my %config = ParseConfig( $path );

   map { $config{$_.'s'} = $config{$_} } ( 'move-vector',
      'shot-vector', 'requires-unit', 'carries-unit' );

   foreach ( 'name', 'is-flying', 'can-rotate',
             'requires-units', 'carries-units' ) {

      my $option = $config{$_};
      next if ( !defined $option );

      # If user has entered multiple values, keep only the last one
      $config{$_} = $option->[-1] if ( ref $option eq 'ARRAY' );
   }

   foreach ( 'move-vectors', 'shot-vectors' ) {
      next if ( !defined $config{$_} );

      # Ensure that single vectors are packaged into an array
      my @vectors = ( ref $config{$_} eq 'ARRAY' )
         ? @{ $config{$_} } : ( $config{$_} );

      @vectors = map {
            my @elements = m{ (-* \d+) }gxms;
            \@elements;
         } @vectors;

      # Add trailing 's' to 'vectors' and set to new arrayref of arrayrefs
      $config{$_} = \@vectors;
   }

   foreach ( 'proxy-move-field', 'proxy-shot-field' ) {
      my $option = $config{$_};
      next if ( !defined $option );

      # If user has entered multiple values, keep only the last one
      $option = $option->[-1] if ( ref $option eq 'ARRAY' );
      my @elements = ( $option =~ m{ \d+ }gxms );

      $config{$_} = \@elements;
   }

   return $class->new( %config );
}

=head2 configure( %options )

 Purpose: Change the values of one or more game piece settings.
 Options: `name' - Name of game piece
          `move-vectors' - ArrayRef of vectors that define game piece movement
          `shot-vectors' - ArrayRef of vectors that define game piece firing
                           range
          `proxy-move-field' - Array defining movement boundaries for carried
                               units
          `proxy-shot-field' - Array defining shot boundaries for carried units
          `requires-units' - Name of unit that this unit moves/shoots via
                             proxy [string].  May also be an arrayref of
                             ChessLike::GamePiece objects.
          `carries-units' - Name of unit that this unit is a move/shot proxy
                            for [string].  May also be an arrayref of
                            ChessLike::GamePiece objects.
          `is-flying' - Boolean value for marking flying units
          `can-rotate' - Boolean value for marking units that can rotate
 Returns: 1
 Throws: none

=cut

sub configure {
   my ( $self, %options ) = @_;

   if ( defined $options{'name'} ) {
      $self->{'name'} = $options{'name'};
   }

   foreach ( 'move-vectors', 'shot-vectors' ) {
      $self->{$_} = $options{$_} if defined $options{$_};
   }

   foreach ( 'proxy-move-field', 'proxy-shot-field',
             'requires-units', 'carries-units' ) {
      $self->{$_} = $options{$_} if exists $options{$_};
   }

   foreach ( 'is-flying', 'can-rotate' ) {
      $self->{$_} = $options{$_} ? 1 : 0 if defined $options{$_};
   }

   return 1;
}

=head2 name()

 Purpose: Returns name of game piece.
 Returns: $name
 Throws: none

=cut

sub name { return shift->{'name'} }

=head2 move_vectors()

 Purpose: Returns ArrayRef of movement vectors for the game piece.
 Returns: $vectors_ref
 Throws: none

=cut

sub move_vectors {
   my $vectors_ref = shift->{'move-vectors'};
   return wantarray ? @{ $vectors_ref } : $vectors_ref;
}

=head2 shot_vectors()

 Purpose: Returns ArrayRef of shot vectors for the game piece.
 Returns: $vectors_ref
 Throws: none

=cut

sub shot_vectors {
   my $vectors_ref = shift->{'shot-vectors'};
   return wantarray ? @{ $vectors_ref } : $vectors_ref;
}

=head2 proxy_move_field()

 Purpose: Returns ArrayRef containing proxy movement field for the game piece.
 Returns: $proxy_move_field_ref or undef
 Throws: none

=cut

sub proxy_move_field {
   return shift->{'proxy-move-field'};
}

=head2 proxy_shot_field()

 Purpose: Returns ArrayRef containing proxy shot field for the game piece.
 Returns: $proxy_shot_field_ref or undef
 Throws: none

=cut

sub proxy_shot_field {
   return shift->{'proxy-shot-field'};
}

=head2 requires_units()

 Purpose: Returns required-units for the game piece.
 Returns: $required_units (string or arrayref) or undef
 Throws: none

=cut

sub requires_units {
   return shift->{'requires-units'};
}

=head2 carries_units()

 Purpose: Returns carries-units for the game piece.
 Returns: $carries_units (string or arrayref) or undef
 Throws: none

=cut

sub carries_units {
   return shift->{'carries-units'};
}

=head2 is_flying()

 Purpose: Returns boolean value that defined whether game piece can fly.
 Returns: 0 or 1
 Throws: none

=cut

sub is_flying { return shift->{'is-flying'} }

=head2 can_rotate()

 Purpose: Returns boolean value that defined whether game piece can rotate.
 Returns: 0 or 1
 Throws: none

=cut

sub can_rotate { return shift->{'can-rotate'} }

=head1 AUTHOR

Michael J. Lewis, C<< <mjlewis at ualberta.net> >>

=head1 ACKNOWLEDGEMENTS

=head1 LICENSE AND COPYRIGHT

Copyright 2012 Michael J. Lewis.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

=cut

1; # End of ChessLike::GamePiece
